////////////////////////////////////////////////////////////////////////////////
//
//  FLEX USER GROUP HOKURIKU
//  Copyright 2009 Flex User Group Hokuriku
//  All Rights Reserved.
// 
//  NOTICE: Flex User Group Hokuriku permits you to use, 
//  modify, and distribute this file 
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package net.fxug.hokuriku.chocolat.service
{
    import com.adobe.serialization.json.JSON;

    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.HTTPStatusEvent;
    import flash.events.IOErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.net.URLRequestHeader;

    import mx.logging.ILogger;

    import net.fxug.hokuriku.chocolat.events.GeoEvent;
    import net.fxug.hokuriku.chocolat.model.LatLng;
    import net.fxug.hokuriku.chocolat.utils.JSONDataParser;
    import net.fxug.hokuriku.chocolat.utils.LoggingUtil;

    /**
     * 検索成功時
     * @eventType net.fxug.hokuriku.chocolat.events.GeoEvent.COMPLETE_EVENT
     */
    [Event(name="completeEvent", type="net.fxug.hokuriku.chocolat.events.GeoEvent")]
    /**
     * 検索失敗時
     * @eventType net.fxug.hokuriku.chocolat.events.GeoEvent.FAILED_EVENT
     */
    [Event(name="failedEvent", type="net.fxug.hokuriku.chocolat.events.GeoEvent")]
    /**
     * ジオコーディング(住所から緯度経度検索)を行う。
     * このAPIはGoogleのGoogleMapAPIを利用しており、http://maps.google.com/maps/geo?にリクエストを投げている。
     * GoogleのジオコーディングAPIは24時間以内の同一IPアドレスからのリクエストを15000件に制限しているので注意すること。
     *
     * @see http://code.google.com/intl/ja/apis/maps/faq.html#geocoder_limit
     *
     * TODO 同一検索時のローカルキャッシュを実装
     * TODO idleTimeoutの実装
     */

    public class GoogleHTTPGeocoder extends EventDispatcher
    {
        /** ジオコーディング用URL */
        public static const MAP_API_GEOCODER:String = "http://maps.google.com/maps/geo?";

        /** 検索対象の住所(必須) */
        public static const QUERY_ADDRESS:String = "q=";
        /** APIキー(必須) */
        public static const QUERY_API_KEY:String = "key=";
        /** 場所センサーでデバイスから取得したものかどうか(デフォルトでfalse) */
        public static const QUERY_SENSOR:String = "sensor=false";
        /** 国別コード(日本を定数として指定) */
        public static const QUERY_GL:String = "gl=ja";

        private static var logger:ILogger = LoggingUtil.getLogger(GoogleHTTPGeocoder);

        /**
         * APIキー
         * http://chocolat.hokuriku.fxug.net/
         */
        private static const API_KEY:String = "ABQIAAAAgi79aEGRh2TG8LzJRaZ8dhR5ycYNVpZ9bcFXYYJY__qGHm_RYhQp255AFtN519BikOl5hUFdb5j_lg";

        /**
         * リファラ
         * Webの単純リクエストであれば問題ないが、AS3のURLRequestでアクセスする場合は
         * APIキーに対応したリファラが設定されていないと駄目っぽいん。
         */
        public static const HEADER_REFERER:URLRequestHeader = new URLRequestHeader("Referer", "http://chocolat.hokuriku.fxug.net/");

        /** リクエストに利用されるURLLoader */
        private var urlLoader:URLLoader;

        /** デフォルトコンストラクタ */
        public function GoogleHTTPGeocoder()
        {
            this.urlLoader = new URLLoader();
            this.urlLoader.addEventListener(Event.COMPLETE, completeHandler);
            this.urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            this.urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            this.urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
        }

        /**
         * ジオコーディング(住所から緯度経度検索)を行う。
         * Googleのジオコーディングに依存しているため、正確性はそっちに聞いてください。
         */
        public function geocode(address:String):void
        {
            if (address == null)
            {
                throw new IllegalOperationError("検索条件は必須項目です。");
            }
            var tmp:String = QUERY_ADDRESS + address + "&" + QUERY_API_KEY + API_KEY + "&" + QUERY_SENSOR + "&" + QUERY_GL;
            var urlRequest:URLRequest = new URLRequest(MAP_API_GEOCODER + tmp);
            // requestHeader 
            urlRequest.requestHeaders.push(HEADER_REFERER);
            this.urlLoader.load(urlRequest);
        }

        public function completeHandler(event:Event):void
        {
            var encodeJSON:Object = JSON.decode(this.urlLoader.data);
            var latLng:LatLng = JSONDataParser.parseGeoCode(encodeJSON);

            logger.debug("Location::{0}", latLng);

            var geoEvent:GeoEvent = new GeoEvent(GeoEvent.COMPLETE_EVENT, latLng);
            this.dispatchEvent(geoEvent);
        }

        public function httpStatusHandler(httpStatusEvent:HTTPStatusEvent):void
        {
            logger.debug("httpStatusHandler status {0}" + httpStatusEvent.status);
        }

        public function ioErrorHandler(ioErrorEvent:IOErrorEvent):void
        {
            logger.error("ioErrorHandler errorId:{0}, text:{1}", ioErrorEvent.errorID, ioErrorEvent.text);
            this.dispatchEvent(new GeoEvent(GeoEvent.FAILED_EVENT, null));
        }

        public function securityErrorHandler(securityErrorEvent:SecurityErrorEvent):void
        {
            logger.error("securityErrorHandler errorId:{0}, text:{1}", securityErrorEvent.errorID, securityErrorEvent.text);
            this.dispatchEvent(new GeoEvent(GeoEvent.FAILED_EVENT, null));
        }
    }
}